package com.droidski.taskdb.model.impl;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.droidski.taskdb.TaskDB;
import com.droidski.taskdb.model.History;
import com.droidski.taskdb.model.Status;
import com.droidski.taskdb.model.Tag;
import com.droidski.taskdb.model.Task;

public class TaskImpl implements Task, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1;

	private static final long VERSION = 3;

	// v1
	String id;
	String alias = "-";
	String name = "-";
	String description = "-";
	long timeStarted;
	long timeCompleted;
	List<History> statusHistory = new ArrayList<History>();
	String parentId;
	List<Tag> tags = new ArrayList<Tag>();
	List<String> children = new ArrayList<String>();
	String location = "-";
	String estimate = "-";
	// v2
	String project = "-";
	String category = "-";
	// v3
	String priority = "-";

	public TaskImpl() {
	}

	@Override
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	@Override
	public String getAlias() {
		return alias;
	}

	@Override
	public void setAlias(String alias) {
		this.alias = alias;
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getDescription() {
		return description;
	}

	@Override
	public void setDescription(String description) {
		this.description = description;
	}

	public long getTimeStarted() {
		return timeStarted;
	}

	public void setTimeStarted(long timeStarted) {
		this.timeStarted = timeStarted;
	}

	public long getTimeCompleted() {
		return timeCompleted;
	}

	public void setTimeCompleted(long timeCompleted) {
		this.timeCompleted = timeCompleted;
	}

	@Override
	public Status getStatus() {
		return statusHistory.get(statusHistory.size() - 1).getStatus();
	}

	@Override
	public void setStatus(Status status, String comment) {
		statusHistory.add(new History(status, comment));
	}

	@Override
	public List<History> getHistory() {
		return statusHistory;
	}

	public void setHistory(List<History> history) {
		this.statusHistory = history;
	}

	@Override
	public Task getParent() {
		return TaskDB.getInstance().findTaskById(parentId);
	}

	@Override
	public void setParent(Task parent) {
		if (parent == null) {
			parentId = null;
		} else {
			this.parentId = parent.getId();
		}
	}

	@Override
	public List<Tag> getTags() {
		return tags;
	}

	@Override
	public List<String> getChildren() {
		return children;
	}

	public void setChildren(ArrayList<String> children) {
		this.children = children;
	}

	@Override
	public String getLocation() {
		return location;
	}

	public void setLocation(String location) {
		this.location = location;
	}

	@Override
	public void pause(String comment) {
		if ( getStatus() != Status.PAUSED ) {
			getHistory().add(new History(Status.PAUSED, comment));
		}
	}

	@Override
	public void complete(String comment) {
		if ( getStatus() != Status.COMPLETED ) {
			getHistory().add(new History(Status.COMPLETED, comment));
		}
	}

	@Override
	public void start(String comment) {
		if ( getStatus() != Status.ACTIVE ) {
			if ( TaskDB.getInstance().getActiveTask() != null ) {
				TaskDB.getInstance().getActiveTask().pause(null);
			}
			getHistory().add(new History(Status.ACTIVE, comment));
		}
	}

	@Override
	public void abandon(String comment) {
		if ( getStatus() != Status.ABANDONED ) {
			getHistory().add(new History(Status.ABANDONED, comment));
		}
	}

	@Override
	public void removeTask(Task task) {
		children.remove(task.getId());
		task.setParent(null);
	}

	@Override
	public void addTask(Task task) {

		String msg = "(add " + task.getId() + " to " + id + ")";

		if (task == this) {
			TaskDB.out("addTask; adding self; can't do it" + msg);
			return;
		}
		if (task.getParent() == this) {
			TaskDB.out("addTask; already a child, not adding again" + msg);
			return;
		}
		if (task.getParent() != null) {
			TaskDB.out("task id " + task.getId() + " has a parent("
					+ task.getParent().getId() + "), removing from there."
					+ msg);
			task.getParent().removeTask(task);
			children.add(task.getId());
			task.setParent(this);

		} else if (children.contains(task.getId())) {
			TaskDB.out("task id " + id
					+ " has no parent, not removing from anything." + msg);
		} else {
			TaskDB.out(msg);
			children.add(task.getId());
			task.setParent(this);
		}
	}

	@Override
	public long getTimeActual() {
		return getTimeActualSince(0);
	}

	public long getTimeActualSince(long start) {
		
		if (getChildren().size() > 0)
			return 0;

		long totalSoFar = 0;
		long lastStart = 0;
		for (History history : statusHistory) {
			if ( history.getTime() > start ) {
				switch (history.getStatus()) {
				case ACTIVE:
					lastStart = history.getTime();
					break;
				case PAUSED:
					totalSoFar += (history.getTime() - lastStart);
					lastStart = 0;
					break;
				case COMPLETED:
					totalSoFar += (history.getTime() - lastStart);
					lastStart = 0;
					break;
				case ABANDONED:
					totalSoFar += (history.getTime() - lastStart);
					lastStart = 0;
					break;
				case NOT_STARTED:
				}
			}
		}

		if (lastStart != 0) {
			totalSoFar = System.currentTimeMillis() - lastStart;
		}

		return totalSoFar;

	}

	@Override
	public long getTimeAccumulated() {
		long totalSoFar = getTimeActual();
		if (getChildren().size() > 0) {
			for (String childId : children) {
				Task child = getTaskDB().findTaskById(childId);
				totalSoFar += child.getTimeAccumulated();
			}
		}
		return totalSoFar;
	}

	@Override
	public long getTimeAccumulatedSince(long start) {
		long totalSoFar = getTimeActualSince(start);
		if (getChildren().size() > 0) {
			for (String childId : children) {
				Task child = getTaskDB().findTaskById(childId);
				totalSoFar += child.getTimeAccumulatedSince(start);
			}
		}
		return totalSoFar;
	}

	public TaskDB getTaskDB() {
		return TaskDB.getInstance();
	}

	/**
	 * returns the time elapsed since this was created
	 */
	@Override
	public long getTimeElapsed() {
		History first = statusHistory.get(0);
		return System.currentTimeMillis() - first.getTime();
	}

	@Override
	public String getEstimate() {
		return estimate;
	}

	@Override
	public void setEstimate(String estimate) {
		this.estimate = estimate;
	}

	@Override
	public String getProject() {
		return project;
	}

	@Override
	public void setProject(String p) {
		this.project = p;
	}

	@Override
	public void setPriority(String p) {
		this.priority = p;
	}

	@Override
	public String getPriority() {
		return priority;
	}

	@Override
	public void setCategory(String p) {
		this.category = p;
	}

	@Override
	public String getCategory() {
		return category;
	}

	private void readObject(ObjectInputStream in)
			throws ClassNotFoundException, IOException {
		// always perform the default de-serialization first
		long thisVersion = in.readLong();
		// if ( thisVersion == VERSION ) {
		id = in.readUTF();
		alias = in.readUTF();
		name = in.readUTF();
		description = in.readUTF();
		timeStarted = in.readLong();
		timeCompleted = in.readLong();
		statusHistory = (List<History>) in.readObject();
		parentId = in.readUTF();
		tags = (List<Tag>) in.readObject();
		children = (List<String>) in.readObject();
		location = in.readUTF();
		estimate = in.readUTF();

		project = "";
		category = "";
		priority = "";

		if (thisVersion > 1) {
			project = in.readUTF();
			category = in.readUTF();
			if (thisVersion > 2) {
				priority = in.readUTF();
			}
		}

	}

	private void writeObject(ObjectOutputStream out) throws IOException {
		out.writeLong(VERSION);
		out.writeUTF("" + id);
		out.writeUTF("" + alias);
		out.writeUTF("" + name);
		out.writeUTF("" + description);
		out.writeLong(timeStarted);
		out.writeLong(timeCompleted);
		out.writeObject(statusHistory);
		out.writeUTF("" + parentId);
		out.writeObject(tags);
		out.writeObject(children);
		out.writeUTF("" + location);
		out.writeUTF("" + estimate);

		out.writeUTF("" + project);
		out.writeUTF("" + category);

		out.writeUTF("" + priority);
	}

	@Override
	public boolean equals(Object o) {
		if (o == null)
			return false;
		if (o instanceof Task) {
			Task task = (Task) o;
			if (task.getId().equals(id))
				return true;
			return false;
		} else {
			return false;
		}
	}
	
	@Override
	public String getParentId() {
		if ( getParent() == null ) {
			return "";
		} else {
			return getParent().getId();
		}
	}
	
	public int getSearchScore(String searchPhrase) {
		
		String lcase = searchPhrase.toLowerCase();
		
		if ( getId().equalsIgnoreCase(searchPhrase) ) {
			return Integer.MAX_VALUE;
		}
		
		if ( getName().equalsIgnoreCase(searchPhrase)) {
			return Integer.MAX_VALUE;
		} else if ( getName().toLowerCase().contains(lcase) ) {
			return Integer.MAX_VALUE / 2;
		}
		
		if ( getDescription().equalsIgnoreCase(searchPhrase) ) {
			return Integer.MAX_VALUE / 4;
		} else if ( getDescription().toLowerCase().equals(lcase) ) {
			return Integer.MAX_VALUE / 8;
		}
		
		return 0;

	}
}
